﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.

namespace Microsoft.Data.Entity.Design.Model.Entity
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Xml.Linq;
    using Microsoft.Data.Entity.Design.Model.Commands;

    internal class EnumType : DocumentableAnnotatableElement
    {
        internal static readonly string ElementName = "EnumType";

        private DefaultableValue<string> _typeAccessAttr;
        private DefaultableValue<bool> _isFlagAttr;
        private DefaultableValue<string> _underlyingTypeAttr;
        private DefaultableValue<string> _externalTypeAttr;

        private readonly List<EnumTypeMember> _members = new List<EnumTypeMember>();

        internal EnumType(ConceptualEntityModel model, XElement element)
            : base(model, element)
        {
        }

        /// <summary>
        ///     Manages the content of the TypeAccess attribute
        /// </summary>
        internal DefaultableValue<string> TypeAccess
        {
            get
            {
                if (_typeAccessAttr == null)
                {
                    _typeAccessAttr = new TypeAccessDefaultableValue(this);
                }
                return _typeAccessAttr;
            }
        }

        /// <summary>
        ///     External Type Name.
        ///     If the value is set, it indicates that the enum type has been defined externally.
        ///     Code generator will not generate Enum type.
        /// </summary>
        internal DefaultableValue<string> ExternalTypeName
        {
            get
            {
                if (_externalTypeAttr == null)
                {
                    _externalTypeAttr = new ExternalTypeNameDefaultableValue(this);
                }
                return _externalTypeAttr;
            }
        }

        internal DefaultableValue<bool> IsFlags
        {
            get
            {
                if (_isFlagAttr == null)
                {
                    _isFlagAttr = new EnumFlagAttributeDefaultableValue(this);
                }
                return _isFlagAttr;
            }
        }

        internal DefaultableValue<string> UnderlyingType
        {
            get
            {
                if (_underlyingTypeAttr == null)
                {
                    _underlyingTypeAttr = new UnderlyingEnumTypeDefaultableValue(this);
                }
                return _underlyingTypeAttr;
            }
        }

        internal void AddMember(EnumTypeMember member)
        {
            _members.Add(member);
        }

        internal IEnumerable<EnumTypeMember> Members()
        {
            foreach (var m in _members)
            {
                yield return m;
            }
        }

        internal EnumTypeMember FindMemberByLocalName(string name)
        {
            foreach (var m in _members)
            {
                if (String.CompareOrdinal(m.LocalName.Value, name) == 0)
                {
                    return m;
                }
            }
            return null;
        }

        // we unfortunately get a warning from the compiler when we use the "base" keyword in "iterator" types generated by using the
        // "yield return" keyword.  By adding this method, I was able to get around this.  Unfortunately, I wasn't able to figure out
        // a way to implement this once and have derived classes share the implementation (since the "base" keyword is resolved at 
        // compile-time and not at runtime.
        private IEnumerable<EFObject> BaseChildren
        {
            get { return base.Children; }
        }

        internal override IEnumerable<EFObject> Children
        {
            get
            {
                foreach (var efobj in BaseChildren)
                {
                    yield return efobj;
                }

                foreach (var child in Members())
                {
                    yield return child;
                }

                yield return TypeAccess;

                yield return UnderlyingType;

                yield return IsFlags;

                yield return ExternalTypeName;
            }
        }

        protected override void OnChildDeleted(EFContainer efContainer)
        {
            var child = efContainer as EnumTypeMember;
            if (child != null)
            {
                _members.Remove(child);
                return;
            }

            base.OnChildDeleted(efContainer);
        }

        protected override void DoNormalize()
        {
            var normalizedName = EntityTypeNameNormalizer.NameNormalizer(this, LocalName.Value);
            Debug.Assert(null != normalizedName, "Null NormalizedName for refName " + LocalName.Value);
            NormalizedName = (normalizedName != null ? normalizedName.Symbol : Symbol.EmptySymbol);
            base.DoNormalize();
        }

#if DEBUG
        internal override ICollection<string> MyChildElementNames()
        {
            var s = base.MyChildElementNames();
            s.Add(EnumTypeMember.ElementName);
            return s;
        }
#endif

#if DEBUG
        internal override ICollection<string> MyAttributeNames()
        {
            var s = base.MyAttributeNames();
            s.Add(TypeAccessDefaultableValue.AttributeTypeAccess);
            s.Add(EnumFlagAttributeDefaultableValue.AttributeIsFlags);
            s.Add(UnderlyingEnumTypeDefaultableValue.AttributeUnderlyingType);
            s.Add(ExternalTypeNameDefaultableValue.AttributeExternalTypeName);
            return s;
        }
#endif

        protected override void PreParse()
        {
            Debug.Assert(State != EFElementState.Parsed, "this object should not already be in the parsed state");

            ClearEFObjectCollection(_members);

            ClearEFObject(_typeAccessAttr);
            _typeAccessAttr = null;

            ClearEFObject(_isFlagAttr);
            _isFlagAttr = null;

            ClearEFObject(_underlyingTypeAttr);
            _underlyingTypeAttr = null;

            ClearEFObject(_externalTypeAttr);
            _externalTypeAttr = null;

            base.PreParse();
        }

        [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
        internal override bool ParseSingleElement(ICollection<XName> unprocessedElements, XElement elem)
        {
            if (elem.Name.LocalName == EnumTypeMember.ElementName)
            {
                var member = new EnumTypeMember(this, elem);
                member.Parse(unprocessedElements);
                AddMember(member);
            }
            else
            {
                return base.ParseSingleElement(unprocessedElements, elem);
            }
            return true;
        }

        internal override DeleteEFElementCommand GetDeleteCommand()
        {
            return new DeleteEnumTypeCommand(this);
        }
    }
}
